<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线PS</title>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="container" id="app">

    <div class="row">
        <h3 class="center">在线图片处理</h3>
    </div>
    <div class="row">
        <input type="file" multiple @change="upload" accept="image/*"/>
    </div>
    <div class="key">
    <div class="row form">
        <h4>图片操作区域</h4>
        图片宽度：<input type="text" class="input" v-model="bg.width">
        图片高度：<input type="text" class="input" v-model="bg.height">
        <button class="sub" @click="draw">确定</button>
        <!-- <button @click="thumb">压缩</button> -->
        <button class="output" @click="output">输出</button>
    </div>
    <div class="set">
        透明度：<input type="range" value="100" min=0 max=100 v-model="f.opacity" @change="set_opacity">
        缩放：<input type="range" value="100" min=10 max=300 v-model="f.zoom" @change="set_zoom">
    </div>
    <div class="row">
        <div class="active-img" :style="style"  @mouseup="move_end" @mousemove="move">
            <img :src="v" v-for="(v,i) in list" class="img" @mousedown="move_start(i,$event)" :style="img_style[i]"/>
        </div>
 
        <div class="output" v-if="show">
            <div class="row form2">
                <h4>图片输出</h4>
                <select v-model="va.status" class="my-select">
                    <option value="rect">遮挡</option>
                    <option value="text">文字</option>
                </select>
                <label><input type="color" class="input" v-model="va.color"></label>
                <button class="download" @click="download">下载</button>
             
            </div>
            <div class="border" :style="style"  @mousedown="rect_start($event)" @mousemove="rect_do($event)" @mouseup="rect_end($event)">
                <canvas :width="bg.width" :height="bg.height" class="canvas"></canvas>
                <div class="rect-border" :style="border_style">
                    <textarea 
                    class="rect-text" 
                    v-model="va.text" 
                    v-if="show_input" 
                    @blur="add_text" 
                    ref="textarea"
                    @keydown="($event)=>{$event.stopPropagation()}"
                    ></textarea>
                </div>
            </div>
        </div>
        
    </div>
    </div>
</div>

</body>
</html>
<script src="./assets/vue2.6.11.min.js"></script>
<style>
.my-select{
    padding:.3rem;
}
.rect-text{
    width:100%;
    height:100%;
    font-size: 20px;
}
.rect-border{
    background:rgba(0,0,0,0);
    border:1px solid #666;
    position: absolute;
    display: none;
}
.form2{
    margin-bottom:.5rem;
}
body{
    width:1200px;
    margin:0 auto;
    padding-bottom:5rem;
}
.center{
    text-align: center;
}
.active-img{
    min-height:400px;
    position: relative;
    border:1px solid #ddd;
}
.img{
    position:absolute;
    cursor: move;
}
.form{
    margin:.5rem 0;
}
.output{
    position: relative;
    z-index: 99999;
    margin-top:1rem;
    background:white;
}
.border{
    border:1px solid #ddd;
    position: relative;
}
.canvas{
    cursor:crosshair;
}
</style>
<script>
/* 
更新：
1. 优化了压缩功能，换成同步的，不需要先创建canvas
2. 输出时才压缩，保证图片质量
3. 解决 z-index 的问题
4. 
TODO:
1. 添加图层功能
2. 添加裁剪功能
3. 添加涂鸦功能
*/    

new Vue({
    el:"#app",
    data:{
        list:[],//'./img/1.jpg',"./img/2.jpg",'./img/14.png', './img/15.png'
        f:{
            width:1000,
            height:740,
            opacity:100,
            zoom:100,
            width:0,
            background:"yelow",
        },
        va:{
            color:"#FF0000",//遮挡的颜色
            status:"rect",//rect 画遮挡矩形，text 添加文字
            text:"",//文字
        },
        show_input:false,
        bg:{
            width:1000,
            height:740,
            background:"white"
        },
        style:{
            width:"1000px",
            height:"740px",
            background:"white",
        },
        ctx:"",
        now_img:"",//正在移动的图片
        active:{
            left:0,
            top:0,
        },
        img_style:[],
        index:-1,
        zindex:0,
        show:false,
        start:false,//是否已经开始移动
        rect:{
            x:0,
            y:0,
            moving:false,
        },
        border_style:{
            display:"none",
            top:0,
            left:0,
            width:0,
            height:0,
        }
    },
    created(){
        this.active={
            left:this.query(".active-img").offsetLeft,
            top:this.query(".active-img").offsetTop,
        }
        this.query("body").onkeydown=(e)=>{
            console.log(e.keyCode)
            if(e.keyCode==38||e.keyCode==40){
                if(e.keyCode==38){
                    this.f.zoom++;
                }else{
                    this.f.zoom--;
                }
                this.set_zoom();
            }
        }
        this.query("body").addEventListener("paste",e=>{
            var items=e.clipboardData.items;
            var file=null;
            for (var i = 0; i < items.length; i++) {
                if (items[i].type.indexOf("image") !== -1) {
                    file = items[i].getAsFile();
                }
            }
            if(file==null) return;
            var img=URL.createObjectURL(file);
            this.list.push(img);
        })
        this.output();
    },
    methods:{
        query(el){
            return document.querySelector(el);
        },
        select(el,i){
            var doms=document.querySelectorAll(el);
            return doms[i];
        },
        thumb(img){
            var img=img?img:this.select(".img",this.index);
            width=img.width?img.width:img.naturalWidth;
            height=width*img.naturalHeight/img.naturalWidth;
            var canvas=document.createElement("canvas");
            canvas.width=width;
            canvas.height=height;
            var ctx=canvas.getContext("2d");
            ctx.drawImage(img,0,0,width,height);
            img.src=canvas.toDataURL();
            return img;//
        },
        output(){
            this.show=false;
            setTimeout(()=>{
                this.show=true;
                setTimeout(()=>{
                    this.ctx=this.query(".canvas").getContext("2d");
                    var doms=document.querySelectorAll(".img");
                    var arr=[];
                    doms.forEach(v=>{
                        if(v.style.width){
                            v=this.thumb(v);
                        }
                        arr.push(v);
                    })
                    arr.sort((a,b)=>{
                        let ai=a.style['z-index']?a.style['z-index']:0;
                        let bi=b.style['z-index']?b.style['z-index']:0;
                        return ai-bi;
                    })
                    //这里为什么还要用异步呢？
                    setTimeout(()=>{
                        arr.forEach(v=>{
                            var x=v.offsetLeft;
                            var y=v.offsetTop;
                            var opacity=v.style.opacity;
                            this.ctx.globalAlpha=opacity?opacity:1;
                            this.ctx.drawImage(v,x,y);
                        })
                    })   
                })
            })
           
        },
        move_start(i,$event){
            // console.log("down",$event)
            this.now_img=this.select(".img",i);
            this.index=i;
            this.zindex++;
            this.now={
                left:$event.clientX-this.now_img.offsetLeft-this.active.left,
                top:$event.clientY-this.now_img.offsetTop-this.active.top,
            }
            this.f.width=this.now_img.offsetWidth;
            this.f.zoom=100;
            if( !(i in this.img_style) ){
                this.img_style[i]={};
            }
            this.img_style[i]['z-index']=this.zindex;
            this.update();
        },
        set_opacity(){
            if(!this.index in this.img_style){
                this.img_style[this.index]={};
            }
            this.img_style[this.index].opacity=this.f.opacity/100;
            this.update();
        },
        update(){
            this.img_style=this.img_style.concat([]);
        },
        set_zoom(){
            if(this.index<0) return;
            var width=this.f.width;
            width=width*this.f.zoom/100;
            this.img_style[this.index].width=width+"px";
            this.update();
        },
        move($event){
            if(!this.now_img) return;
            // console.log("move",$event)
            var left=$event.clientX-this.now.left-this.active.left;
            var top=$event.clientY-this.now.top-this.active.top;
            if(!this.index in this.img_style){
                 this.img_style[this.index]={};
            }
            this.img_style[this.index].left=left+"px";
            this.img_style[this.index].top=top+"px";
            this.update();
            // console.log(this.img_style)
            $event.preventDefault();
        },
        move_end(e){
            // console.log("up",e)
            this.now_img="";
        },
        draw(){
            this.style={
                width:this.bg.width+"px",
                height:this.bg.height+"px",
                background:this.bg.background,
            }
        },
        upload(e){
            var files=e.target.files;
            for(var i in files){
                let v=files[i];
                if(!v.size) continue;
                var reader=new FileReader();
                reader.readAsDataURL(v);
                reader.onload=e=>{
                    var img=e.target.result;
                    var left=this.list.length<3?this.list.length*333:666;
                    this.list.push(img);
                    this.img_style.push({
                        width:'333px',
                        left:left+"px",
                    })
                }
            } 
        },
        rect_start(e){
            if(this.show_input) return;
            this.rect.moving=true;
            this.rect.x=e.offsetX;
            this.rect.y=e.offsetY;
            this.border_style['display']="block";
            this.border_style['left']=this.rect.x+"px";
            this.border_style['top']=this.rect.y+"px";
            this.border_style['width']=0;
            this.border_style['height']=0;
            console.log(this.rect.x,this.rect.y)
        },
        rect_do(e){
            if(this.rect.moving==false) return;
            //第一种，这种效果没那么大，但宽度和高度却e.offsetX,e.offsetY 数值不一样，出来的效果却一样，为什么？
            // this.border_style.width=e.offsetX-this.rect.x+"px";
            // this.border_style.height=e.offsetY-this.rect.y+"px";

            //第二种，这个放大后可以缩小，上一个不可以
            this.border_style.width=e.offsetX+"px";
            this.border_style.height=e.offsetY+"px";

            //为什么以上两种明明数值不一样，但出来的效果却是一样的？
            // console.log(e.offsetX,this.rect.x,this.border_style.width);

            e.preventDefault();
        },
        rect_end(e){
            this.rect.moving=false;
            //这里的offsetX和offsetY 是相对mousedown的，且取绝对值
            if(e.offsetX<5 || e.offsetY<5) return;
            this.ctx.fillStyle=this.va.color;
            if(this.va.status=='rect'){
                this.ctx.fillRect(this.rect.x,this.rect.y,e.offsetX,e.offsetY);
                this.border_style['display']="none";
            }else{
                if(this.border_style['display']=="none") return;
                this.show_input=true;
                this.va.text="";
                // setTimeout(()=>{
                //     this.$refs.textarea.focus();
                // })
            }
        },
        add_text(){
            this.ctx.fillStyle=this.va.color;
            this.ctx.font="20px Arial";
            this.ctx.textBaseline="hanging";
            // var width=this.ctx.measureText(this.va.text).width;
            var width=this.border_style.width.replace("px","");
            var len=Math.ceil(width/20);
            var start=0;
            var row=1;
            //中文显示没有大问题，但英文和数字占格没那么多
            while(start<this.va.text.length){
                var text=this.va.text.substr(start,len);
                var y=Math.floor(start/len)*20;
                console.log(text,y)
                this.ctx.fillText(text,this.rect.x,this.rect.y+y);
                start+=len;
                row++;
            }
            this.show_input=false;
            this.border_style['display']="none";
            // this.ctx.strokeStyle=this.va.color;
            console.log(width)
            // this.ctx.strokeRect(this.rect.x-5,this.rect.y-5,parseInt(width)+10,20*row);
        },
        download(){
            var img=this.query(".canvas").toDataURL("image/png");
            this.download_true(img);
        },
        download_true(url){
            var oA = document.createElement("a");
            oA.download = '';// 设置下载的文件名，默认是'下载'
            oA.href = url;
            document.body.appendChild(oA);
            oA.click();
            oA.remove(); // 下载之后把创建的元素删除
        }
    }
})

</script>